package oop.ex3.filemanager.actions;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.Set;

import oop.ex3.messages.*;
import oop.ex3.messages.exception.InvalidHeaderException;
import oop.ex3.messages.exception.UnexpectedMessageEndException;

import oop.ex3.filemanager.FMContext;
import oop.ex3.filemanager.actions.exceptions.InvalidNumOfParamsException;
import oop.ex3.filemanager.actions.exceptions.RecievedError;
import oop.ex3.nameserver.exception.UnexpectedMessageException;
import oop.ex3.sockets.ClientSocket;

public class FmAction {
	
	// context object containing data collections and commangs
	protected FMContext _context;
	
	/**
	 * constructor for FmAction
	 * @param context 
	 */
	public FmAction(FMContext context){
		_context = context;
	}
	
	/**
	 * run action- must be implemented by every action
	 * @param params
	 * @throws InvalidNumOfParamsException
	 * @throws InterruptedException
	 * @throws UnknownHostException
	 * @throws IOException
	 */
	public void run(String[] params) 
	throws InvalidNumOfParamsException, InterruptedException,
	UnknownHostException, IOException{
		
	}
	
	/**
	 * wait for done message from server
	 * @param s socket to read from
	 * @throws IOException
	 * @throws UnexpectedMessageEndException
	 * @throws UnexpectedMessageException
	 */
	public void waitForDone(ClientSocket s)
	throws IOException, UnexpectedMessageEndException,
	UnexpectedMessageException{
		MessageInterface msg = s.readMessage();
		if (!msg.getHeader().equals(ValidHeaders.DONE)){
			//TODO this is from a different exception package
			throw new UnexpectedMessageException();
		}
	}
	
	/**
	 * send End session and wait for done
	 * @param s socket to read/write from
	 */
	public void finishSession(ClientSocket s){
		try {
			s.SendMessage(new SimpleMessage(ValidHeaders.ENDSESSION));
			waitForDone(s);
//			s.close();
		} catch (IOException e) {
			
		} catch (UnexpectedMessageEndException e) {
			s.closeClientSocket();
		} catch (UnexpectedMessageException e) {
			s.closeClientSocket();
		} catch (InvalidHeaderException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	/**
	 * send Begin and send data if server replies with WELCOME
	 * @param s socket to write to/read from
	 * @throws IOException
	 * @throws UnexpectedMessageEndException
	 * @throws UnexpectedMessageException
	 * @throws RecievedError
	 * @throws InvalidHeaderException 
	 */
	public void performHandShake(ClientSocket s) 
	throws IOException, UnexpectedMessageEndException, 
	UnexpectedMessageException, RecievedError, InvalidHeaderException{
		//send begin
		s.SendMessage(new PeerInfoMessage(ValidHeaders.BEGIN, 
				new InetSocketAddress(InetAddress.getLocalHost(), 
						_context.getPort())));
		MessageInterface msg;
		if ((msg = s.readMessage()).getHeader()
				.equals(ValidHeaders.WELCOME)){
			sendInfo(s);
		} 
		if (msg.getHeader().equals(ValidHeaders.ERROR)){
			throw new RecievedError();
		}
		if (msg.getHeader().equals(ValidHeaders.DONE)){
			return;
		}
		else {
			//TODO
//			throw new UnexpectedMessage();
		}
	}

	/**
	 * send files and servers info to a server
	 * @param s socket to write to
	 * @throws IOException
	 * @throws UnexpectedMessageEndException
	 * @throws UnexpectedMessageException
	 * @throws InvalidHeaderException 
	 */
	public void sendInfo(ClientSocket s) throws IOException, 
	UnexpectedMessageEndException, UnexpectedMessageException, InvalidHeaderException {
		LinkedHashSet<InetSocketAddress> servers = _context.getServersSet();
		Set<String> files = _context.getFilesNames();
		for (String file : files){
			s.SendMessage(new FileMessage(ValidHeaders.CONTAINFILE, file));
			waitForDone(s);
		}
		s.SendMessage(new SimpleMessage(ValidHeaders.ENDLIST));
		waitForDone(s);
		for (InetSocketAddress server1 : servers){
			s.SendMessage(new PeerInfoMessage(
				ValidHeaders.CONTAINNAMESERVER, server1));
			waitForDone(s);
		}
		s.SendMessage(new SimpleMessage(ValidHeaders.ENDLIST));
		waitForDone(s);
	}

}
